1 /* 2 * Copyright (C) 2012-2013 たかはしのんき. All rights reserved. 3 * 4 * History: 5 * 0.5 2013-06-15 JsDoc Toolkit対応。得点の表示。 6 * 0.4 2013-04-07 石の数の表示。 7 * 0.3 2013-03-29 活路の表示。 8 * 0.2 2012-05-17 レイヤーに対応。局面オブジェクト対応。 9 * 0.1 2012-05-15 新規作成。 goban02.js より独立。星の表示に対応。 10 */ 11 12 var PIXEL_COL = 22; 13 var PIXEL_ROW = 24; 14 var PIXEL_CHAR = 24; 15 16 /** 17 * @fileOverview Board - 碁盤オブジェクト 18 * @version 0.5 19 * @author たかはしのんき 20 * 21 */ 22 23 /** 24 * 碁盤オブジェクトを作成します。 25 * @constructor 26 * @this {Board} 27 * @param ro 路数 28 * @param context 描画先のコンテキスト 29 * @since 0.1 30 */ 31 Board = function(ro, context) { 32 this.ro = ro; 33 this.boardSize = { // 碁盤のサイズ[ピクセル] 34 width : PIXEL_COL * (ro + 1), 35 height : PIXEL_ROW * (ro + 1) 36 }; 37 this.boardOffset = { // 碁盤のテーブルからのオフセット[ピクセル] 38 x : Math.floor((context.canvas.width - this.boardSize.width) / 2), 39 y : Math.floor((context.canvas.height 40 + PIXEL_CHAR * 2 - this.boardSize.height) / 2) 41 }; 42 this.gridSize = { // 格子全体のサイズ[ピクセル] 43 width : PIXEL_COL * (ro - 1) + 2, 44 height : PIXEL_ROW * (ro - 1) + 2 45 }; 46 this.gridOffset = { // 格子全体の碁盤からのオフセット[ピクセル] 47 x : Math.floor((this.boardSize.width - this.gridSize.width) / 2), 48 y : Math.floor((this.boardSize.height - this.gridSize.height) / 2) 49 }; 50 this.p = new Pos(ro); // 局面オブジェクト 51 this.prisoner = new Array(2); // アゲハマ 52 }; 53 54 Board.prototype = { 55 56 /** 57 * 着手します。 58 * @param col 石の桁位置を指定します。 59 * @param row 石の行位置を指定します。 60 * @param context3 石を描画するコンテキスト 61 * @param context2 影を描画するコンテキスト 62 * @since 0.2 63 */ 64 move : function(col, row, context3, context2) { 65 passButton[this.p.turn].disabled = true; 66 var turn = this.p.turn; 67 var moved = this.p.move(col, row); // 着手禁止点は false 68 var mv = new Move(col, row); 69 if (!mv.isPass(this.ro)) { 70 // setWatch(); 71 if (moved) { 72 this.prisoner[P_BLACK] = this.p.prisoner[P_BLACK]; 73 this.prisoner[P_WHITE] = this.p.prisoner[P_WHITE]; 74 // rec.move(col, row); 75 this.drawStone(col, row, turn, context3, context2); 76 auClick.play(); 77 if (this.p.d.abs()) { 78 this.removeStones(this.p.d, this.p.turn, context3, context2); 79 showPrisoner(turn, this.prisoner[turn - 1]); 80 } 81 showScore(BLACK, this.p.liberty[P_BLACK] + this.p.w.inv().abs()); 82 showScore(WHITE, this.p.liberty[P_WHITE] + this.p.b.inv().abs()); 83 } else 84 auError.play(); 85 } 86 passButton[this.p.turn].disabled = false; 87 return moved; 88 }, 89 90 /** 91 * 机上のオフセット座標から碁盤の[パス]ボタンの処理 92 * @since 0.2 93 */ 94 offsetToMove : function(x, y) { 95 var col = Math.floor((x - this.boardOffset.x - this.gridOffset.x / 2) / PIXEL_COL) + 1; 96 var row = Math.floor((y - this.boardOffset.y - this.gridOffset.y / 2) / PIXEL_ROW) + 1; 97 var mv = new Move(col, row); 98 if (1 <= col && col <= this.ro && 1 <= row && row <= this.ro) 99 mv.onBoard = true; 100 else 101 mv.onBoard = false; 102 return mv; 103 }, 104 105 /** 106 * 碁盤を初期状態に戻します。 107 * @since 0.2 108 */ 109 clear : function() { 110 this.p.clear(); // 局面オブジェクト 111 this.prisoner = [0, 0]; // アゲハマ 112 }, 113 114 /** 115 * 碁盤を描画します。 116 * @param s 影の長さ(高さ/2) 117 * @param context 描画先のコンテキスト 118 * @since 0.1 119 */ 120 drawBoard : function(s, context) { 121 var x = this.boardOffset.x; 122 var y = this.boardOffset.y; 123 var ro = this.ro; 124 var dx = PIXEL_COL; 125 var dy = PIXEL_ROW; 126 var width = this.boardSize.width; 127 var height = this.boardSize.height; 128 var gwidth = this.gridSize.width; 129 var gheight = this.gridSize.height; 130 var gx = x + this.gridOffset.x; 131 var gy = y + this.gridOffset.y; 132 // 碁盤の影 133 context.beginPath(); 134 context.moveTo(x, y); 135 context.lineTo(x + width, y); 136 context.lineTo(x + width + s, y + s); 137 context.lineTo(x + width + s, y + height + s); 138 context.lineTo(x + s, y + height + s); 139 context.lineTo(x, y + height); 140 context.closePath(); 141 context.fillStyle = "black"; 142 context.globalAlpha = 0.5; 143 context.fill(); 144 // 碁盤 145 context.beginPath(); 146 context.rect(x, y, width, height); 147 context.fillStyle = "burlywood"; 148 context.globalAlpha = 1.0; 149 context.fill(); 150 // 木目 151 this.drawWoodGrain(x, y, width, height, context); 152 // 格子 153 context.fillStyle = "black"; 154 var x1, y1, lwidth; 155 // (横の格子線) 156 x1 = gx; 157 for (var row = 1; row <= ro; row++) { 158 if (row == 1) 159 y1 = gy + (row - 1) * dy; 160 else 161 y1 = gy + 1 + (row - 1) * dy; 162 if (row == 1 || row == ro) 163 lwidth = 2; 164 else 165 lwidth = 1; 166 context.beginPath(); 167 context.rect(x1, y1, gwidth, lwidth); 168 context.fill(); 169 } 170 // 縦の格子線 171 y1 = gy; 172 for (var col = 1; col <= ro; col++) { 173 if (col == 1) 174 x1 = gx + (col - 1) * dx; 175 else 176 x1 = gx + 1 + (col - 1) * dx; 177 if (col == 1 || col == ro) 178 lwidth = 2; 179 else 180 lwidth = 1; 181 context.beginPath(); 182 context.rect(x1, y1, lwidth, gheight); 183 context.fill(); 184 } 185 if (ro % 2 == 1) { // 天元 186 this.drawStar(Math.floor(ro / 2) + 1, Math.floor(ro / 2) + 1, context); 187 } 188 if (ro == 9) { // 3線隅の星 189 this.drawStar(3, 3, context); 190 this.drawStar(ro - 2, 3, context); 191 this.drawStar(3, ro - 2, context); 192 this.drawStar(ro - 2, ro - 2, context); 193 } 194 if (ro == 13 || ro == 19) { // 4線隅の星 195 this.drawStar(4, 4, context); 196 this.drawStar(ro - 3, 4, context); 197 this.drawStar(4, ro - 3, context); 198 this.drawStar(ro - 3, ro - 3, context); 199 } 200 if (ro == 19) { // 4線辺の星 201 this.drawStar(ro / 2 + 1, 4, context); 202 this.drawStar(4, ro / 2 + 1, context); 203 this.drawStar(ro - 3, ro / 2 + 1, context); 204 this.drawStar(ro / 2 + 1, ro - 3, context); 205 } 206 }, 207 208 /** 209 * 石を描画します。 210 * @param col 石の桁位置を指定します。 211 * @param row 石の行位置を指定します。 212 * @param stone 石 (BLACK か WHITE) を指定します。 213 * @param context3 描画先のコンテキスト 214 * @param context2 影を描画するコンテキスト 215 * @since 0.2 216 */ 217 drawStone : function(col, row, stone, context3, context2) { 218 var x = this.boardOffset.x + this.gridOffset.x + 1 + 219 (col - 1) * PIXEL_COL; 220 var y = this.boardOffset.y + this.gridOffset.y + 1 + 221 (row - 1) * PIXEL_ROW; 222 if (stone == BLACK) 223 drawObject(x - (PX_STONE / 2), y - (PX_STONE / 2), Z2_STONE, imgb, context3, context2); 224 else if (stone == WHITE) 225 drawObject(x - (PX_STONE / 2), y - (PX_STONE / 2), Z2_STONE, imgw, context3, context2); 226 }, 227 228 /** 229 * 石を消します。 230 * @param d 取られる石を表す BVecter を指定します。 231 * @param stone 取られる石 (BLACK か WHITE) を指定します。 232 * @param context3 描画先のコンテキスト 233 * @param context2 影を描画するコンテキスト 234 * @since 0.2 235 */ 236 removeStones : function(d, stone, context3, context2) { 237 for (var i = 1; i <= d.order; i++) 238 if (d.getValue(i) == 1) { 239 var mv = this.p.toMove(i); 240 var x = this.boardOffset.x + this.gridOffset.x + 1 + 241 (mv.col - 1) * PIXEL_COL; 242 var y = this.boardOffset.y + this.gridOffset.y + 1 + 243 (mv.row - 1) * PIXEL_ROW; 244 context3.clearRect(x - (PX_STONE / 2), y - (PX_STONE / 2), PIXEL_COL, PIXEL_ROW); 245 context2.clearRect(x - (PX_STONE / 2) + Z2_STONE, y - (PX_STONE / 2) + Z2_STONE, PIXEL_COL, PIXEL_ROW); 246 // 碁笥の蓋にアゲハマのイメージを表示 247 var r = Math.random() * PX_LID / 3; 248 var a = Math.random() * 2 * Math.PI; 249 if (stone == BLACK) { 250 var xs = offsetWLid.x + Math.floor((PX_LID / 2) + r * Math.cos(a) - (PX_STONE / 2)); 251 var ys = offsetWLid.y + Math.floor((PX_LID / 2) + r * Math.sin(a) - (PX_STONE / 2)); 252 drawObject(xs, ys, Z2_STONE, imgb, context2, context2); 253 } else { 254 var xs = offsetBLid.x + Math.floor((PX_LID / 2) + r * Math.cos(a) - (PX_STONE / 2)); 255 var ys = offsetBLid.y + Math.floor((PX_LID / 2) + r * Math.sin(a) - (PX_STONE / 2)); 256 drawObject(xs, ys, Z2_STONE, imgw, context2, context2); 257 } 258 } 259 }, 260 /** 261 * 星を描画します。 262 * @param col 星の桁位置を指定します。 263 * @param row 星の行位置を指定します。 264 * @param context 描画先のコンテキスト 265 * @since 0.1 266 */ 267 drawStar : function(col, row, context) { 268 var x = this.boardOffset.x + this.gridOffset.x + 1 + 269 (col - 1) * PIXEL_COL - 2; 270 var y = this.boardOffset.y + this.gridOffset.y + 1 + 271 (row - 1) * PIXEL_ROW - 2; 272 context.drawImage(imgs, x, y); 273 }, 274 275 /** 276 * 木目を描画します。 277 * @param x 左端座標 278 * @param y 上端座標 279 * @param width 描画する幅 280 * @param height 描画する高さ 281 * @param context 描画先のコンテキストを指定します。 282 * @since 0.1 283 */ 284 drawWoodGrain : function(x, y, width, height, context) { 285 context.strokeStyle = "rgb(192, 153, 86)"; // 木目の色 286 var xb0 = x; 287 var xb1 = x + width; 288 var yb0 = y; 289 var yb1 = y + height; 290 var xo, yo, xw, yw; 291 var xc = xb0 + (xb1 - xb0) * 5 / 7; // 木目の中心 292 var yc = yb0 + (yb1 - yb0) * 5 / 4; 293 for (var rw = 5; rw <= 460; rw += 5) { 294 xo = xc + rw; 295 yo = yc; 296 for (var theta = 0; theta <= 2 * Math.PI; 297 theta += 2 * Math.PI / 19) { 298 xw = xc + Math.floor(rw * Math.cos(theta)); 299 yw = yc + 13 * Math.floor(rw * Math.sin(theta)); 300 var xw0 = xo, xw1 = xw, yw0 = yo, yw1 = yw; 301 if (yw1<yb0 && yb0<yw0) { //上辺でクリッピング 302 xw1 = xw0+(xw1-xw0)*(yb0-yw0)/(yw1-yw0); 303 yw1 = yb0; 304 } else if (yw0<yb0 && yb0<yw1) { //上辺でクリッピング 305 xw0 = xw1+(xw0-xw1)*(yb0-yw1)/(yw0-yw1); 306 yw0 = yb0; 307 } 308 if (yw1<yb1 && yb1<yw0) { //下辺でクリッピング 309 xw0 = xw1+(xw0-xw1)*(yb1-yw1)/(yw0-yw1); 310 yw0 = yb1; 311 } else if (yw0<yb1 && yb1<yw1) { //下辺でクリッピング 312 xw1 = xw0+(xw1-xw0)*(yb1-yw0)/(yw1-yw0); 313 yw1 = yb1; 314 } 315 if (xw1<xb0 && xb0<xw0) { //左辺でクリッピング 316 xw1 = xb0; 317 yw1 = yw0+(yw1-yw0)*(xb0-xw0)/(xw1-xw0); 318 } else if (xw0<xb0 && xb0<xw1) { //左辺でクリッピング 319 xw0 = xb0; 320 yw0 = yw1+(yw0-yw1)*(xb0-xw1)/(xw0-xw1); 321 } 322 if (xw1<xb1 && xb1<xw0) { //右辺でクリッピング 323 xw0 = xb1; 324 yw0 = yw1+(yw0-yw1)*(xb1-xw1)/(xw0-xw1); 325 } else if (xw0<xb1 && xb1<xw1) { //右辺でクリッピング 326 xw1 = xb1; 327 yw1 = yw0+(yw1-yw0)*(xb1-xw0)/(xw1-xw0); 328 } 329 if (xb0<=xw0 && xw0<=xb1 && xb0<=xw1 && xw1<=xb1 && 330 yb0<=yw0 && yw0<=yb1 && yb0<=yw1 && yw1<=yb1) { 331 context.beginPath(); 332 context.moveTo(xw0, yw0); 333 context.lineTo(xw1, yw1); 334 context.stroke(); 335 } 336 xo = xw; 337 yo = yw; 338 } 339 } 340 } 341 }; 342